{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Data transformation and pipelines\n",
    "\n",
    "Skforecast has two arguments in all the forecasters that allow more detailed control over input data transformations. This feature is particularly useful as many machine learning models require specific data pre-processing transformations. For example, linear models may benefit from features being scaled, or categorical features being encoded into numerical values.\n",
    "\n",
    "+  `transformer_y`: an instance of a transformer (preprocessor) compatible with the scikit-learn preprocessing API with the methods: fit, transform, fit_transform and, inverse_transform. Scikit-learn ColumnTransformer is not allowed since they do not have the inverse_transform method. If multiple transformations are needed, a scikit-learn pipeline can be used. For example, scaling the target variable and applying a logarithmic transformation.\n",
    "\n",
    "+ `transformer_exog`: an instance of a transformer (preprocessor) compatible with the scikit-learn preprocessing API. Scikit-learn ColumnTransformer can be used if the preprocessing transformations only apply to some specific columns or if different transformations are needed for different columns. For example, scale numeric features and one hot encode categorical ones. It is also possible to use a scikit-learn pipeline to apply multiple transformations sequentially.\n",
    "\n",
    "Transformations are learned and applied before training the forecaster and are automatically used when calling `predict`. The output of `predict` is always on the same scale as the original series *y*."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Libraries\n",
    "# ==============================================================================\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from lightgbm import LGBMRegressor\n",
    "from sklearn.linear_model import Ridge\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sklearn.preprocessing import OneHotEncoder\n",
    "from sklearn.preprocessing import MinMaxScaler\n",
    "from sklearn.preprocessing import PowerTransformer\n",
    "from sklearn.preprocessing import FunctionTransformer\n",
    "from sklearn.compose import ColumnTransformer\n",
    "from sklearn.pipeline import Pipeline\n",
    "from skforecast.datasets import fetch_dataset\n",
    "from skforecast.recursive import ForecasterRecursive\n",
    "from skforecast.recursive import ForecasterRecursiveMultiSeries\n",
    "from skforecast.model_selection import TimeSeriesFold\n",
    "from skforecast.model_selection import grid_search_forecaster"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">╭─────────────────────────────────── <span style=\"font-weight: bold\">h2o_exog</span> ────────────────────────────────────╮\n",
       "│ <span style=\"font-weight: bold\">Description:</span>                                                                    │\n",
       "│ Monthly expenditure ($AUD) on corticosteroid drugs that the Australian health   │\n",
       "│ system had between 1991 and 2008. Two additional variables (exog_1, exog_2) are │\n",
       "│ simulated.                                                                      │\n",
       "│                                                                                 │\n",
       "│ <span style=\"font-weight: bold\">Source:</span>                                                                         │\n",
       "│ Hyndman R (2023). fpp3: Data for Forecasting: Principles and Practice (3rd      │\n",
       "│ Edition). http://pkg.robjhyndman.com/fpp3package/,                              │\n",
       "│ https://github.com/robjhyndman/fpp3package, http://OTexts.com/fpp3.             │\n",
       "│                                                                                 │\n",
       "│ <span style=\"font-weight: bold\">URL:</span>                                                                            │\n",
       "│ https://raw.githubusercontent.com/skforecast/skforecast-                        │\n",
       "│ datasets/main/data/h2o_exog.csv                                                 │\n",
       "│                                                                                 │\n",
       "│ <span style=\"font-weight: bold\">Shape:</span> 195 rows x 3 columns                                                     │\n",
       "╰─────────────────────────────────────────────────────────────────────────────────╯\n",
       "</pre>\n"
      ],
      "text/plain": [
       "╭─────────────────────────────────── \u001b[1mh2o_exog\u001b[0m ────────────────────────────────────╮\n",
       "│ \u001b[1mDescription:\u001b[0m                                                                    │\n",
       "│ Monthly expenditure ($AUD) on corticosteroid drugs that the Australian health   │\n",
       "│ system had between 1991 and 2008. Two additional variables (exog_1, exog_2) are │\n",
       "│ simulated.                                                                      │\n",
       "│                                                                                 │\n",
       "│ \u001b[1mSource:\u001b[0m                                                                         │\n",
       "│ Hyndman R (2023). fpp3: Data for Forecasting: Principles and Practice (3rd      │\n",
       "│ Edition). http://pkg.robjhyndman.com/fpp3package/,                              │\n",
       "│ https://github.com/robjhyndman/fpp3package, http://OTexts.com/fpp3.             │\n",
       "│                                                                                 │\n",
       "│ \u001b[1mURL:\u001b[0m                                                                            │\n",
       "│ https://raw.githubusercontent.com/skforecast/skforecast-                        │\n",
       "│ datasets/main/data/h2o_exog.csv                                                 │\n",
       "│                                                                                 │\n",
       "│ \u001b[1mShape:\u001b[0m 195 rows x 3 columns                                                     │\n",
       "╰─────────────────────────────────────────────────────────────────────────────────╯\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Download data\n",
    "# ==============================================================================\n",
    "data = fetch_dataset(\"h2o_exog\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>y</th>\n",
       "      <th>exog_1</th>\n",
       "      <th>exog_2</th>\n",
       "      <th>exog_3</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>date</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1992-04-01</th>\n",
       "      <td>0.379808</td>\n",
       "      <td>0.958792</td>\n",
       "      <td>1.166029</td>\n",
       "      <td>A</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1992-05-01</th>\n",
       "      <td>0.361801</td>\n",
       "      <td>0.951993</td>\n",
       "      <td>1.117859</td>\n",
       "      <td>A</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1992-06-01</th>\n",
       "      <td>0.410534</td>\n",
       "      <td>0.952955</td>\n",
       "      <td>1.067942</td>\n",
       "      <td>A</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1992-07-01</th>\n",
       "      <td>0.483389</td>\n",
       "      <td>0.958078</td>\n",
       "      <td>1.097376</td>\n",
       "      <td>A</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1992-08-01</th>\n",
       "      <td>0.475463</td>\n",
       "      <td>0.956370</td>\n",
       "      <td>1.122199</td>\n",
       "      <td>A</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                   y    exog_1    exog_2 exog_3\n",
       "date                                           \n",
       "1992-04-01  0.379808  0.958792  1.166029      A\n",
       "1992-05-01  0.361801  0.951993  1.117859      A\n",
       "1992-06-01  0.410534  0.952955  1.067942      A\n",
       "1992-07-01  0.483389  0.958078  1.097376      A\n",
       "1992-08-01  0.475463  0.956370  1.122199      A"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Data preprocessing\n",
    "# ==============================================================================\n",
    "data.index.name = 'date'\n",
    "# Add an extra categorical variable\n",
    "data['exog_3'] = ([\"A\"] * int(len(data) / 2)) + ([\"B\"] * (int(len(data) / 2) + 1))\n",
    "data.head()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Transforming input series\n",
    "\n",
    "The following example shows how to include a transformer that scales the input series *y*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "    <style>\n",
       "        .container-374c6478918a438f8f58bc65e94578b6 {\n",
       "            font-family: 'Arial', sans-serif;\n",
       "            font-size: 0.9em;\n",
       "            color: #333333;\n",
       "            border: 1px solid #ddd;\n",
       "            background-color: #f0f8ff;\n",
       "            padding: 5px 15px;\n",
       "            border-radius: 8px;\n",
       "            max-width: 600px;\n",
       "            #margin: auto;\n",
       "        }\n",
       "        .container-374c6478918a438f8f58bc65e94578b6 h2 {\n",
       "            font-size: 1.5em;\n",
       "            color: #222222;\n",
       "            border-bottom: 2px solid #ddd;\n",
       "            padding-bottom: 5px;\n",
       "            margin-bottom: 15px;\n",
       "            margin-top: 5px;\n",
       "        }\n",
       "        .container-374c6478918a438f8f58bc65e94578b6 details {\n",
       "            margin: 10px 0;\n",
       "        }\n",
       "        .container-374c6478918a438f8f58bc65e94578b6 summary {\n",
       "            font-weight: bold;\n",
       "            font-size: 1.1em;\n",
       "            color: #000000;\n",
       "            cursor: pointer;\n",
       "            margin-bottom: 5px;\n",
       "            background-color: #b3dbfd;\n",
       "            padding: 5px;\n",
       "            border-radius: 5px;\n",
       "        }\n",
       "        .container-374c6478918a438f8f58bc65e94578b6 summary:hover {\n",
       "            color: #000000;\n",
       "            background-color: #e0e0e0;\n",
       "        }\n",
       "        .container-374c6478918a438f8f58bc65e94578b6 ul {\n",
       "            font-family: 'Courier New', monospace;\n",
       "            list-style-type: none;\n",
       "            padding-left: 20px;\n",
       "            margin: 10px 0;\n",
       "            line-height: normal;\n",
       "        }\n",
       "        .container-374c6478918a438f8f58bc65e94578b6 li {\n",
       "            margin: 5px 0;\n",
       "            font-family: 'Courier New', monospace;\n",
       "        }\n",
       "        .container-374c6478918a438f8f58bc65e94578b6 li strong {\n",
       "            font-weight: bold;\n",
       "            color: #444444;\n",
       "        }\n",
       "        .container-374c6478918a438f8f58bc65e94578b6 li::before {\n",
       "            content: \"- \";\n",
       "            color: #666666;\n",
       "        }\n",
       "        .container-374c6478918a438f8f58bc65e94578b6 a {\n",
       "            color: #001633;\n",
       "            text-decoration: none;\n",
       "        }\n",
       "        .container-374c6478918a438f8f58bc65e94578b6 a:hover {\n",
       "            color: #359ccb; \n",
       "        }\n",
       "    </style>\n",
       "    \n",
       "        <div class=\"container-374c6478918a438f8f58bc65e94578b6\">\n",
       "            <p style=\"font-size: 1.5em; font-weight: bold; margin-block-start: 0.83em; margin-block-end: 0.83em;\">ForecasterRecursive</p>\n",
       "            <details open>\n",
       "                <summary>General Information</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Estimator:</strong> Ridge</li>\n",
       "                    <li><strong>Lags:</strong> [1 2 3]</li>\n",
       "                    <li><strong>Window features:</strong> None</li>\n",
       "                    <li><strong>Window size:</strong> 3</li>\n",
       "                    <li><strong>Series name:</strong> y</li>\n",
       "                    <li><strong>Exogenous included:</strong> True</li>\n",
       "                    <li><strong>Weight function included:</strong> False</li>\n",
       "                    <li><strong>Differentiation order:</strong> None</li>\n",
       "                    <li><strong>Creation date:</strong> 2025-11-27 12:01:49</li>\n",
       "                    <li><strong>Last fit date:</strong> 2025-11-27 12:01:49</li>\n",
       "                    <li><strong>Skforecast version:</strong> 0.19.0</li>\n",
       "                    <li><strong>Python version:</strong> 3.12.11</li>\n",
       "                    <li><strong>Forecaster id:</strong> None</li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Exogenous Variables</summary>\n",
       "                <ul>\n",
       "                    exog_1, exog_2\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Data Transformations</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Transformer for y:</strong> StandardScaler()</li>\n",
       "                    <li><strong>Transformer for exog:</strong> None</li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Training Information</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Training range:</strong> [Timestamp('1992-04-01 00:00:00'), Timestamp('2008-06-01 00:00:00')]</li>\n",
       "                    <li><strong>Training index type:</strong> DatetimeIndex</li>\n",
       "                    <li><strong>Training index frequency:</strong> <MonthBegin></li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Estimator Parameters</summary>\n",
       "                <ul>\n",
       "                    {'alpha': 1.0, 'copy_X': True, 'fit_intercept': True, 'max_iter': None, 'positive': False, 'random_state': 123, 'solver': 'auto', 'tol': 0.0001}\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Fit Kwargs</summary>\n",
       "                <ul>\n",
       "                    {}\n",
       "                </ul>\n",
       "            </details>\n",
       "            <p>\n",
       "                <a href=\"https://skforecast.org/0.19.0/api/forecasterrecursive.html\">&#128712 <strong>API Reference</strong></a>\n",
       "                &nbsp;&nbsp;\n",
       "                <a href=\"https://skforecast.org/0.19.0/user_guides/autoregressive-forecaster.html\">&#128462 <strong>User Guide</strong></a>\n",
       "            </p>\n",
       "        </div>\n",
       "        "
      ],
      "text/plain": [
       "=================== \n",
       "ForecasterRecursive \n",
       "=================== \n",
       "Estimator: Ridge \n",
       "Lags: [1 2 3] \n",
       "Window features: None \n",
       "Window size: 3 \n",
       "Series name: y \n",
       "Exogenous included: True \n",
       "Exogenous names: exog_1, exog_2 \n",
       "Transformer for y: StandardScaler() \n",
       "Transformer for exog: None \n",
       "Weight function included: False \n",
       "Differentiation order: None \n",
       "Training range: [Timestamp('1992-04-01 00:00:00'), Timestamp('2008-06-01 00:00:00')] \n",
       "Training index type: DatetimeIndex \n",
       "Training index frequency: <MonthBegin> \n",
       "Estimator parameters: \n",
       "    {'alpha': 1.0, 'copy_X': True, 'fit_intercept': True, 'max_iter': None,\n",
       "    'positive': False, 'random_state': 123, 'solver': 'auto', 'tol': 0.0001} \n",
       "fit_kwargs: {} \n",
       "Creation date: 2025-11-27 12:01:49 \n",
       "Last fit date: 2025-11-27 12:01:49 \n",
       "Skforecast version: 0.19.0 \n",
       "Python version: 3.12.11 \n",
       "Forecaster id: None "
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create and fit forecaster that scales the input series\n",
    "# ==============================================================================\n",
    "forecaster = ForecasterRecursive(\n",
    "                 estimator        = Ridge(random_state=123),\n",
    "                 lags             = 3,\n",
    "                 transformer_y    = StandardScaler(),\n",
    "                 transformer_exog = None\n",
    "             )\n",
    "\n",
    "forecaster.fit(y=data['y'], exog=data[['exog_1', 'exog_2']])\n",
    "forecaster"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Transforming exogenous variables\n",
    "\n",
    "The following example shows how to apply the same transformation (scaling) to all exogenous variables."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "    <style>\n",
       "        .container-e583c955a7714a3292e539857252a2f4 {\n",
       "            font-family: 'Arial', sans-serif;\n",
       "            font-size: 0.9em;\n",
       "            color: #333333;\n",
       "            border: 1px solid #ddd;\n",
       "            background-color: #f0f8ff;\n",
       "            padding: 5px 15px;\n",
       "            border-radius: 8px;\n",
       "            max-width: 600px;\n",
       "            #margin: auto;\n",
       "        }\n",
       "        .container-e583c955a7714a3292e539857252a2f4 h2 {\n",
       "            font-size: 1.5em;\n",
       "            color: #222222;\n",
       "            border-bottom: 2px solid #ddd;\n",
       "            padding-bottom: 5px;\n",
       "            margin-bottom: 15px;\n",
       "            margin-top: 5px;\n",
       "        }\n",
       "        .container-e583c955a7714a3292e539857252a2f4 details {\n",
       "            margin: 10px 0;\n",
       "        }\n",
       "        .container-e583c955a7714a3292e539857252a2f4 summary {\n",
       "            font-weight: bold;\n",
       "            font-size: 1.1em;\n",
       "            color: #000000;\n",
       "            cursor: pointer;\n",
       "            margin-bottom: 5px;\n",
       "            background-color: #b3dbfd;\n",
       "            padding: 5px;\n",
       "            border-radius: 5px;\n",
       "        }\n",
       "        .container-e583c955a7714a3292e539857252a2f4 summary:hover {\n",
       "            color: #000000;\n",
       "            background-color: #e0e0e0;\n",
       "        }\n",
       "        .container-e583c955a7714a3292e539857252a2f4 ul {\n",
       "            font-family: 'Courier New', monospace;\n",
       "            list-style-type: none;\n",
       "            padding-left: 20px;\n",
       "            margin: 10px 0;\n",
       "            line-height: normal;\n",
       "        }\n",
       "        .container-e583c955a7714a3292e539857252a2f4 li {\n",
       "            margin: 5px 0;\n",
       "            font-family: 'Courier New', monospace;\n",
       "        }\n",
       "        .container-e583c955a7714a3292e539857252a2f4 li strong {\n",
       "            font-weight: bold;\n",
       "            color: #444444;\n",
       "        }\n",
       "        .container-e583c955a7714a3292e539857252a2f4 li::before {\n",
       "            content: \"- \";\n",
       "            color: #666666;\n",
       "        }\n",
       "        .container-e583c955a7714a3292e539857252a2f4 a {\n",
       "            color: #001633;\n",
       "            text-decoration: none;\n",
       "        }\n",
       "        .container-e583c955a7714a3292e539857252a2f4 a:hover {\n",
       "            color: #359ccb; \n",
       "        }\n",
       "    </style>\n",
       "    \n",
       "        <div class=\"container-e583c955a7714a3292e539857252a2f4\">\n",
       "            <p style=\"font-size: 1.5em; font-weight: bold; margin-block-start: 0.83em; margin-block-end: 0.83em;\">ForecasterRecursive</p>\n",
       "            <details open>\n",
       "                <summary>General Information</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Estimator:</strong> Ridge</li>\n",
       "                    <li><strong>Lags:</strong> [1 2 3]</li>\n",
       "                    <li><strong>Window features:</strong> None</li>\n",
       "                    <li><strong>Window size:</strong> 3</li>\n",
       "                    <li><strong>Series name:</strong> y</li>\n",
       "                    <li><strong>Exogenous included:</strong> True</li>\n",
       "                    <li><strong>Weight function included:</strong> False</li>\n",
       "                    <li><strong>Differentiation order:</strong> None</li>\n",
       "                    <li><strong>Creation date:</strong> 2025-11-27 12:01:50</li>\n",
       "                    <li><strong>Last fit date:</strong> 2025-11-27 12:01:50</li>\n",
       "                    <li><strong>Skforecast version:</strong> 0.19.0</li>\n",
       "                    <li><strong>Python version:</strong> 3.12.11</li>\n",
       "                    <li><strong>Forecaster id:</strong> None</li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Exogenous Variables</summary>\n",
       "                <ul>\n",
       "                    exog_1, exog_2\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Data Transformations</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Transformer for y:</strong> None</li>\n",
       "                    <li><strong>Transformer for exog:</strong> StandardScaler()</li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Training Information</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Training range:</strong> [Timestamp('1992-04-01 00:00:00'), Timestamp('2008-06-01 00:00:00')]</li>\n",
       "                    <li><strong>Training index type:</strong> DatetimeIndex</li>\n",
       "                    <li><strong>Training index frequency:</strong> <MonthBegin></li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Estimator Parameters</summary>\n",
       "                <ul>\n",
       "                    {'alpha': 1.0, 'copy_X': True, 'fit_intercept': True, 'max_iter': None, 'positive': False, 'random_state': 123, 'solver': 'auto', 'tol': 0.0001}\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Fit Kwargs</summary>\n",
       "                <ul>\n",
       "                    {}\n",
       "                </ul>\n",
       "            </details>\n",
       "            <p>\n",
       "                <a href=\"https://skforecast.org/0.19.0/api/forecasterrecursive.html\">&#128712 <strong>API Reference</strong></a>\n",
       "                &nbsp;&nbsp;\n",
       "                <a href=\"https://skforecast.org/0.19.0/user_guides/autoregressive-forecaster.html\">&#128462 <strong>User Guide</strong></a>\n",
       "            </p>\n",
       "        </div>\n",
       "        "
      ],
      "text/plain": [
       "=================== \n",
       "ForecasterRecursive \n",
       "=================== \n",
       "Estimator: Ridge \n",
       "Lags: [1 2 3] \n",
       "Window features: None \n",
       "Window size: 3 \n",
       "Series name: y \n",
       "Exogenous included: True \n",
       "Exogenous names: exog_1, exog_2 \n",
       "Transformer for y: None \n",
       "Transformer for exog: StandardScaler() \n",
       "Weight function included: False \n",
       "Differentiation order: None \n",
       "Training range: [Timestamp('1992-04-01 00:00:00'), Timestamp('2008-06-01 00:00:00')] \n",
       "Training index type: DatetimeIndex \n",
       "Training index frequency: <MonthBegin> \n",
       "Estimator parameters: \n",
       "    {'alpha': 1.0, 'copy_X': True, 'fit_intercept': True, 'max_iter': None,\n",
       "    'positive': False, 'random_state': 123, 'solver': 'auto', 'tol': 0.0001} \n",
       "fit_kwargs: {} \n",
       "Creation date: 2025-11-27 12:01:50 \n",
       "Last fit date: 2025-11-27 12:01:50 \n",
       "Skforecast version: 0.19.0 \n",
       "Python version: 3.12.11 \n",
       "Forecaster id: None "
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create and fit forecaster with same tranformation for all exogenous variables\n",
    "# ==============================================================================\n",
    "forecaster = ForecasterRecursive(\n",
    "                 estimator        = Ridge(random_state=123),\n",
    "                 lags             = 3,\n",
    "                 transformer_y    = None,\n",
    "                 transformer_exog = StandardScaler()\n",
    "             )\n",
    "\n",
    "forecaster.fit(y=data['y'], exog=data[['exog_1', 'exog_2']])\n",
    "forecaster"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is also possible to apply a different transformation to each exogenous variable making use of `ColumnTransformer`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "    <style>\n",
       "        .container-e1ebd89ff733425ba4ea59c9845b720b {\n",
       "            font-family: 'Arial', sans-serif;\n",
       "            font-size: 0.9em;\n",
       "            color: #333333;\n",
       "            border: 1px solid #ddd;\n",
       "            background-color: #f0f8ff;\n",
       "            padding: 5px 15px;\n",
       "            border-radius: 8px;\n",
       "            max-width: 600px;\n",
       "            #margin: auto;\n",
       "        }\n",
       "        .container-e1ebd89ff733425ba4ea59c9845b720b h2 {\n",
       "            font-size: 1.5em;\n",
       "            color: #222222;\n",
       "            border-bottom: 2px solid #ddd;\n",
       "            padding-bottom: 5px;\n",
       "            margin-bottom: 15px;\n",
       "            margin-top: 5px;\n",
       "        }\n",
       "        .container-e1ebd89ff733425ba4ea59c9845b720b details {\n",
       "            margin: 10px 0;\n",
       "        }\n",
       "        .container-e1ebd89ff733425ba4ea59c9845b720b summary {\n",
       "            font-weight: bold;\n",
       "            font-size: 1.1em;\n",
       "            color: #000000;\n",
       "            cursor: pointer;\n",
       "            margin-bottom: 5px;\n",
       "            background-color: #b3dbfd;\n",
       "            padding: 5px;\n",
       "            border-radius: 5px;\n",
       "        }\n",
       "        .container-e1ebd89ff733425ba4ea59c9845b720b summary:hover {\n",
       "            color: #000000;\n",
       "            background-color: #e0e0e0;\n",
       "        }\n",
       "        .container-e1ebd89ff733425ba4ea59c9845b720b ul {\n",
       "            font-family: 'Courier New', monospace;\n",
       "            list-style-type: none;\n",
       "            padding-left: 20px;\n",
       "            margin: 10px 0;\n",
       "            line-height: normal;\n",
       "        }\n",
       "        .container-e1ebd89ff733425ba4ea59c9845b720b li {\n",
       "            margin: 5px 0;\n",
       "            font-family: 'Courier New', monospace;\n",
       "        }\n",
       "        .container-e1ebd89ff733425ba4ea59c9845b720b li strong {\n",
       "            font-weight: bold;\n",
       "            color: #444444;\n",
       "        }\n",
       "        .container-e1ebd89ff733425ba4ea59c9845b720b li::before {\n",
       "            content: \"- \";\n",
       "            color: #666666;\n",
       "        }\n",
       "        .container-e1ebd89ff733425ba4ea59c9845b720b a {\n",
       "            color: #001633;\n",
       "            text-decoration: none;\n",
       "        }\n",
       "        .container-e1ebd89ff733425ba4ea59c9845b720b a:hover {\n",
       "            color: #359ccb; \n",
       "        }\n",
       "    </style>\n",
       "    \n",
       "        <div class=\"container-e1ebd89ff733425ba4ea59c9845b720b\">\n",
       "            <p style=\"font-size: 1.5em; font-weight: bold; margin-block-start: 0.83em; margin-block-end: 0.83em;\">ForecasterRecursive</p>\n",
       "            <details open>\n",
       "                <summary>General Information</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Estimator:</strong> Ridge</li>\n",
       "                    <li><strong>Lags:</strong> [1 2 3]</li>\n",
       "                    <li><strong>Window features:</strong> None</li>\n",
       "                    <li><strong>Window size:</strong> 3</li>\n",
       "                    <li><strong>Series name:</strong> y</li>\n",
       "                    <li><strong>Exogenous included:</strong> True</li>\n",
       "                    <li><strong>Weight function included:</strong> False</li>\n",
       "                    <li><strong>Differentiation order:</strong> None</li>\n",
       "                    <li><strong>Creation date:</strong> 2025-11-27 12:01:50</li>\n",
       "                    <li><strong>Last fit date:</strong> 2025-11-27 12:01:50</li>\n",
       "                    <li><strong>Skforecast version:</strong> 0.19.0</li>\n",
       "                    <li><strong>Python version:</strong> 3.12.11</li>\n",
       "                    <li><strong>Forecaster id:</strong> None</li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Exogenous Variables</summary>\n",
       "                <ul>\n",
       "                    exog_1, exog_2, exog_3\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Data Transformations</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Transformer for y:</strong> None</li>\n",
       "                    <li><strong>Transformer for exog:</strong> ColumnTransformer(remainder='passthrough',\n",
       "                  transformers=[('scale_1', StandardScaler(), ['exog_1']),\n",
       "                                ('scale_2', StandardScaler(), ['exog_2']),\n",
       "                                ('onehot', OneHotEncoder(), ['exog_3'])],\n",
       "                  verbose_feature_names_out=False)</li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Training Information</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Training range:</strong> [Timestamp('1992-04-01 00:00:00'), Timestamp('2008-06-01 00:00:00')]</li>\n",
       "                    <li><strong>Training index type:</strong> DatetimeIndex</li>\n",
       "                    <li><strong>Training index frequency:</strong> <MonthBegin></li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Estimator Parameters</summary>\n",
       "                <ul>\n",
       "                    {'alpha': 1.0, 'copy_X': True, 'fit_intercept': True, 'max_iter': None, 'positive': False, 'random_state': 123, 'solver': 'auto', 'tol': 0.0001}\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Fit Kwargs</summary>\n",
       "                <ul>\n",
       "                    {}\n",
       "                </ul>\n",
       "            </details>\n",
       "            <p>\n",
       "                <a href=\"https://skforecast.org/0.19.0/api/forecasterrecursive.html\">&#128712 <strong>API Reference</strong></a>\n",
       "                &nbsp;&nbsp;\n",
       "                <a href=\"https://skforecast.org/0.19.0/user_guides/autoregressive-forecaster.html\">&#128462 <strong>User Guide</strong></a>\n",
       "            </p>\n",
       "        </div>\n",
       "        "
      ],
      "text/plain": [
       "=================== \n",
       "ForecasterRecursive \n",
       "=================== \n",
       "Estimator: Ridge \n",
       "Lags: [1 2 3] \n",
       "Window features: None \n",
       "Window size: 3 \n",
       "Series name: y \n",
       "Exogenous included: True \n",
       "Exogenous names: exog_1, exog_2, exog_3 \n",
       "Transformer for y: None \n",
       "Transformer for exog: ColumnTransformer(remainder='passthrough',\n",
       "                  transformers=[('scale_1', StandardScaler(), ['exog_1']),\n",
       "                                ('scale_2', StandardScaler(), ['exog_2']),\n",
       "                                ('onehot', OneHotEncoder(), ['exog_3'])],\n",
       "                  verbose_feature_names_out=False) \n",
       "Weight function included: False \n",
       "Differentiation order: None \n",
       "Training range: [Timestamp('1992-04-01 00:00:00'), Timestamp('2008-06-01 00:00:00')] \n",
       "Training index type: DatetimeIndex \n",
       "Training index frequency: <MonthBegin> \n",
       "Estimator parameters: \n",
       "    {'alpha': 1.0, 'copy_X': True, 'fit_intercept': True, 'max_iter': None,\n",
       "    'positive': False, 'random_state': 123, 'solver': 'auto', 'tol': 0.0001} \n",
       "fit_kwargs: {} \n",
       "Creation date: 2025-11-27 12:01:50 \n",
       "Last fit date: 2025-11-27 12:01:50 \n",
       "Skforecast version: 0.19.0 \n",
       "Python version: 3.12.11 \n",
       "Forecaster id: None "
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create and fit forecaster with different transformations for each exog variable\n",
    "# ==============================================================================\n",
    "transformer_exog = ColumnTransformer(\n",
    "                       [('scale_1', StandardScaler(), ['exog_1']),\n",
    "                        ('scale_2', StandardScaler(), ['exog_2']),\n",
    "                        ('onehot', OneHotEncoder(), ['exog_3']),\n",
    "                       ],\n",
    "                       remainder = 'passthrough',\n",
    "                       verbose_feature_names_out = False\n",
    "                   )\n",
    "\n",
    "forecaster = ForecasterRecursive(\n",
    "                 estimator        = Ridge(random_state=123),\n",
    "                 lags             = 3,\n",
    "                 transformer_y    = None,\n",
    "                 transformer_exog = transformer_exog\n",
    "             )\n",
    "\n",
    "forecaster.fit(y=data['y'], exog=data[['exog_1', 'exog_2', 'exog_3']])\n",
    "forecaster"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is possible to verify if the data transformation has been applied correctly by examining the training matrices. The training matrices should reflect the data transformation that was specified using the `transformer_y` or `transformer_exog` arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Inspect training matrices\n",
    "# ==============================================================================\n",
    "X_train, y_train = forecaster.create_train_X_y(\n",
    "                       y    = data['y'],\n",
    "                       exog = data[['exog_1', 'exog_2', 'exog_3']]\n",
    "                   )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>lag_1</th>\n",
       "      <th>lag_2</th>\n",
       "      <th>lag_3</th>\n",
       "      <th>exog_1</th>\n",
       "      <th>exog_2</th>\n",
       "      <th>exog_3_A</th>\n",
       "      <th>exog_3_B</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>date</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1992-07-01</th>\n",
       "      <td>0.410534</td>\n",
       "      <td>0.361801</td>\n",
       "      <td>0.379808</td>\n",
       "      <td>-2.119529</td>\n",
       "      <td>-2.135088</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1992-08-01</th>\n",
       "      <td>0.483389</td>\n",
       "      <td>0.410534</td>\n",
       "      <td>0.361801</td>\n",
       "      <td>-2.131024</td>\n",
       "      <td>-1.996017</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1992-09-01</th>\n",
       "      <td>0.475463</td>\n",
       "      <td>0.483389</td>\n",
       "      <td>0.410534</td>\n",
       "      <td>-2.109222</td>\n",
       "      <td>-1.822392</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1992-10-01</th>\n",
       "      <td>0.534761</td>\n",
       "      <td>0.475463</td>\n",
       "      <td>0.483389</td>\n",
       "      <td>-2.132137</td>\n",
       "      <td>-1.590667</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "               lag_1     lag_2     lag_3    exog_1    exog_2  exog_3_A  \\\n",
       "date                                                                     \n",
       "1992-07-01  0.410534  0.361801  0.379808 -2.119529 -2.135088       1.0   \n",
       "1992-08-01  0.483389  0.410534  0.361801 -2.131024 -1.996017       1.0   \n",
       "1992-09-01  0.475463  0.483389  0.410534 -2.109222 -1.822392       1.0   \n",
       "1992-10-01  0.534761  0.475463  0.483389 -2.132137 -1.590667       1.0   \n",
       "\n",
       "            exog_3_B  \n",
       "date                  \n",
       "1992-07-01       0.0  \n",
       "1992-08-01       0.0  \n",
       "1992-09-01       0.0  \n",
       "1992-10-01       0.0  "
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X_train.head(4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "date\n",
       "1992-07-01    0.483389\n",
       "1992-08-01    0.475463\n",
       "1992-09-01    0.534761\n",
       "1992-10-01    0.568606\n",
       "Freq: MS, Name: y, dtype: float64"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_train.head(4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"admonition note\" name=\"html-admonition\" style=\"background: rgba(0,191,191,.1); padding-top: 0px; padding-bottom: 6px; border-radius: 8px; border-left: 8px solid #00bfa5; border-color: #00bfa5; padding-left: 10px; padding-right: 10px;\">\n",
    "\n",
    "<p class=\"title\">\n",
    "    <i style=\"font-size: 18px; color:#00bfa5;\"></i>\n",
    "    <b style=\"color: #00bfa5;\">&#128161 Tip</b>\n",
    "</p>\n",
    "\n",
    "To learn more about how to extract the training and prediction matrices, visit the following link: <a href=\"../user_guides/training-and-prediction-matrices.html\">How to Extract Training and Prediction Matrices</a>.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sklearn Pipelines and ColumnTransformer"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Both `transformer_y` and `transformer_exog` accept scikit-learn pipelines. This allows for multiple transformations to be applied sequentially. The following example shows how to scale the target variable and apply a box-cox transformation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "    <style>\n",
       "        .container-2c014c0b3a3c44c4b3ed35e97b58e969 {\n",
       "            font-family: 'Arial', sans-serif;\n",
       "            font-size: 0.9em;\n",
       "            color: #333333;\n",
       "            border: 1px solid #ddd;\n",
       "            background-color: #f0f8ff;\n",
       "            padding: 5px 15px;\n",
       "            border-radius: 8px;\n",
       "            max-width: 600px;\n",
       "            #margin: auto;\n",
       "        }\n",
       "        .container-2c014c0b3a3c44c4b3ed35e97b58e969 h2 {\n",
       "            font-size: 1.5em;\n",
       "            color: #222222;\n",
       "            border-bottom: 2px solid #ddd;\n",
       "            padding-bottom: 5px;\n",
       "            margin-bottom: 15px;\n",
       "            margin-top: 5px;\n",
       "        }\n",
       "        .container-2c014c0b3a3c44c4b3ed35e97b58e969 details {\n",
       "            margin: 10px 0;\n",
       "        }\n",
       "        .container-2c014c0b3a3c44c4b3ed35e97b58e969 summary {\n",
       "            font-weight: bold;\n",
       "            font-size: 1.1em;\n",
       "            color: #000000;\n",
       "            cursor: pointer;\n",
       "            margin-bottom: 5px;\n",
       "            background-color: #b3dbfd;\n",
       "            padding: 5px;\n",
       "            border-radius: 5px;\n",
       "        }\n",
       "        .container-2c014c0b3a3c44c4b3ed35e97b58e969 summary:hover {\n",
       "            color: #000000;\n",
       "            background-color: #e0e0e0;\n",
       "        }\n",
       "        .container-2c014c0b3a3c44c4b3ed35e97b58e969 ul {\n",
       "            font-family: 'Courier New', monospace;\n",
       "            list-style-type: none;\n",
       "            padding-left: 20px;\n",
       "            margin: 10px 0;\n",
       "            line-height: normal;\n",
       "        }\n",
       "        .container-2c014c0b3a3c44c4b3ed35e97b58e969 li {\n",
       "            margin: 5px 0;\n",
       "            font-family: 'Courier New', monospace;\n",
       "        }\n",
       "        .container-2c014c0b3a3c44c4b3ed35e97b58e969 li strong {\n",
       "            font-weight: bold;\n",
       "            color: #444444;\n",
       "        }\n",
       "        .container-2c014c0b3a3c44c4b3ed35e97b58e969 li::before {\n",
       "            content: \"- \";\n",
       "            color: #666666;\n",
       "        }\n",
       "        .container-2c014c0b3a3c44c4b3ed35e97b58e969 a {\n",
       "            color: #001633;\n",
       "            text-decoration: none;\n",
       "        }\n",
       "        .container-2c014c0b3a3c44c4b3ed35e97b58e969 a:hover {\n",
       "            color: #359ccb; \n",
       "        }\n",
       "    </style>\n",
       "    \n",
       "        <div class=\"container-2c014c0b3a3c44c4b3ed35e97b58e969\">\n",
       "            <p style=\"font-size: 1.5em; font-weight: bold; margin-block-start: 0.83em; margin-block-end: 0.83em;\">ForecasterRecursive</p>\n",
       "            <details open>\n",
       "                <summary>General Information</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Estimator:</strong> Ridge</li>\n",
       "                    <li><strong>Lags:</strong> [1 2 3]</li>\n",
       "                    <li><strong>Window features:</strong> None</li>\n",
       "                    <li><strong>Window size:</strong> 3</li>\n",
       "                    <li><strong>Series name:</strong> y</li>\n",
       "                    <li><strong>Exogenous included:</strong> True</li>\n",
       "                    <li><strong>Weight function included:</strong> False</li>\n",
       "                    <li><strong>Differentiation order:</strong> None</li>\n",
       "                    <li><strong>Creation date:</strong> 2025-11-27 12:01:50</li>\n",
       "                    <li><strong>Last fit date:</strong> 2025-11-27 12:01:50</li>\n",
       "                    <li><strong>Skforecast version:</strong> 0.19.0</li>\n",
       "                    <li><strong>Python version:</strong> 3.12.11</li>\n",
       "                    <li><strong>Forecaster id:</strong> None</li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Exogenous Variables</summary>\n",
       "                <ul>\n",
       "                    exog_1, exog_2, exog_3\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Data Transformations</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Transformer for y:</strong> Pipeline(steps=[('scaler', StandardScaler()), ('power', PowerTransformer())])</li>\n",
       "                    <li><strong>Transformer for exog:</strong> ColumnTransformer(remainder='passthrough',\n",
       "                  transformers=[('scale_1', StandardScaler(), ['exog_1']),\n",
       "                                ('scale_2', StandardScaler(), ['exog_2']),\n",
       "                                ('onehot', OneHotEncoder(), ['exog_3'])],\n",
       "                  verbose_feature_names_out=False)</li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Training Information</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Training range:</strong> [Timestamp('1992-04-01 00:00:00'), Timestamp('2008-06-01 00:00:00')]</li>\n",
       "                    <li><strong>Training index type:</strong> DatetimeIndex</li>\n",
       "                    <li><strong>Training index frequency:</strong> <MonthBegin></li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Estimator Parameters</summary>\n",
       "                <ul>\n",
       "                    {'alpha': 1.0, 'copy_X': True, 'fit_intercept': True, 'max_iter': None, 'positive': False, 'random_state': 123, 'solver': 'auto', 'tol': 0.0001}\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Fit Kwargs</summary>\n",
       "                <ul>\n",
       "                    {}\n",
       "                </ul>\n",
       "            </details>\n",
       "            <p>\n",
       "                <a href=\"https://skforecast.org/0.19.0/api/forecasterrecursive.html\">&#128712 <strong>API Reference</strong></a>\n",
       "                &nbsp;&nbsp;\n",
       "                <a href=\"https://skforecast.org/0.19.0/user_guides/autoregressive-forecaster.html\">&#128462 <strong>User Guide</strong></a>\n",
       "            </p>\n",
       "        </div>\n",
       "        "
      ],
      "text/plain": [
       "=================== \n",
       "ForecasterRecursive \n",
       "=================== \n",
       "Estimator: Ridge \n",
       "Lags: [1 2 3] \n",
       "Window features: None \n",
       "Window size: 3 \n",
       "Series name: y \n",
       "Exogenous included: True \n",
       "Exogenous names: exog_1, exog_2, exog_3 \n",
       "Transformer for y: Pipeline(steps=[('scaler', StandardScaler()), ('power', PowerTransformer())]) \n",
       "Transformer for exog: ColumnTransformer(remainder='passthrough',\n",
       "                  transformers=[('scale_1', StandardScaler(), ['exog_1']),\n",
       "                                ('scale_2', StandardScaler(), ['exog_2']),\n",
       "                                ('onehot', OneHotEncoder(), ['exog_3'])],\n",
       "                  verbose_feature_names_out=False) \n",
       "Weight function included: False \n",
       "Differentiation order: None \n",
       "Training range: [Timestamp('1992-04-01 00:00:00'), Timestamp('2008-06-01 00:00:00')] \n",
       "Training index type: DatetimeIndex \n",
       "Training index frequency: <MonthBegin> \n",
       "Estimator parameters: \n",
       "    {'alpha': 1.0, 'copy_X': True, 'fit_intercept': True, 'max_iter': None,\n",
       "    'positive': False, 'random_state': 123, 'solver': 'auto', 'tol': 0.0001} \n",
       "fit_kwargs: {} \n",
       "Creation date: 2025-11-27 12:01:50 \n",
       "Last fit date: 2025-11-27 12:01:50 \n",
       "Skforecast version: 0.19.0 \n",
       "Python version: 3.12.11 \n",
       "Forecaster id: None "
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Multiple transformations using pipelines and column transformers\n",
    "# ==============================================================================\n",
    "transformer_y = Pipeline(\n",
    "                    steps = [\n",
    "                        ('scaler', StandardScaler()),\n",
    "                        ('power', PowerTransformer(method='yeo-johnson'))\n",
    "                    ]\n",
    "                )\n",
    "\n",
    "transformer_exog = ColumnTransformer(\n",
    "                       [('scale_1', StandardScaler(), ['exog_1']),\n",
    "                        ('scale_2', StandardScaler(), ['exog_2']),\n",
    "                        ('onehot', OneHotEncoder(), ['exog_3']),\n",
    "                       ],\n",
    "                       remainder = 'passthrough',\n",
    "                       verbose_feature_names_out = False\n",
    "                   )\n",
    "\n",
    "forecaster = ForecasterRecursive(\n",
    "                    estimator        = Ridge(random_state=123),\n",
    "                    lags             = 3,\n",
    "                    transformer_y    = transformer_y,\n",
    "                    transformer_exog = transformer_exog\n",
    "                )\n",
    "forecaster.fit(y=data['y'], exog=data[['exog_1', 'exog_2', 'exog_3']])\n",
    "forecaster"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Custom transformers"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using scikit-learn [FunctionTransformer](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.FunctionTransformer.html) it is possible to include custom transformers in the forecaster object, for example, a logarithmic transformation. \n",
    "\n",
    "\n",
    "Scikit-learn's [FunctionTransformer](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.FunctionTransformer.html) can be used to incorporate custom transformers, such as a logarithmic transformation, in the forecaster object. To implement this, a user-defined transformation function can be created and then passed to the `FunctionTransformer`. Detailed information on how to use FunctionTransformer can be found in the [scikit-learn documentation](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.FunctionTransformer.html)."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"admonition note\" name=\"html-admonition\" style=\"background: rgba(255,145,0,.1); padding-top: 0px; padding-bottom: 6px; border-radius: 8px; border-left: 8px solid #ff9100; border-color: #ff9100; padding-left: 10px; padding-right: 10px\">\n",
    "\n",
    "<p class=\"title\">\n",
    "    <i style=\"font-size: 18px; color:#ff9100; border-color: #ff1744;\"></i>\n",
    "    <b style=\"color: #ff9100;\"> <span style=\"color: #ff9100;\">&#9888;</span> Warning</b>\n",
    "</p>\n",
    "\n",
    "For versions 1.1.0 >= scikit-learn <= 1.2.0 <code>sklearn.preprocessing.FunctionTransformer.inverse_transform</code> does not support DataFrames that are all numerical when <code>check_inverse=True</code>. It will raise an Exception which is fixed in scikit-learn 1.2.1.\n",
    "\n",
    "More info: https://scikit-learn.org/stable/whats_new/v1.2.html#version-1-2-1\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create custom transformer\n",
    "# =============================================================================\n",
    "def log_transform(x):\n",
    "    \"\"\" \n",
    "    Calculate log adding 1 to avoid calculation errors if x is very close to 0.\n",
    "    \"\"\"\n",
    "    return np.log(x + 1)\n",
    "\n",
    "\n",
    "def exp_transform(x):\n",
    "    \"\"\"\n",
    "    Inverse of log_transform.\n",
    "    \"\"\"\n",
    "    return np.exp(x) - 1\n",
    "\n",
    "\n",
    "transformer_y = FunctionTransformer(func=log_transform, inverse_func=exp_transform)\n",
    "\n",
    "# Create and train forecaster\n",
    "# ==============================================================================\n",
    "forecaster = ForecasterRecursive(\n",
    "                 estimator     = Ridge(random_state=123),\n",
    "                 lags          = 3,\n",
    "                 transformer_y = transformer_y\n",
    "             )\n",
    "\n",
    "forecaster.fit(y=data['y'])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If the `FunctionTransformer` has an inverse function, the output of the predict method is automatically transformed back to the original scale."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2008-07-01    0.776206\n",
       "2008-08-01    0.775471\n",
       "2008-09-01    0.777200\n",
       "2008-10-01    0.777853\n",
       "Freq: MS, Name: pred, dtype: float64"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "forecaster.predict(steps=4)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pipelines as estimators"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"admonition note\" name=\"html-admonition\" style=\"background: rgba(255,145,0,.1); padding-top: 0px; padding-bottom: 6px; border-radius: 8px; border-left: 8px solid #ff9100; border-color: #ff9100; padding-left: 10px; padding-right: 10px\">\n",
    "\n",
    "<p class=\"title\">\n",
    "    <i style=\"font-size: 18px; color:#ff9100; border-color: #ff1744;\"></i>\n",
    "    <b style=\"color: #ff9100;\"> <span style=\"color: #ff9100;\">&#9888;</span> Warning</b>\n",
    "</p>\n",
    "\n",
    "Skforecast allows the usage of scikit-learn pipelines as estimators. It is important to note that ColumnTransformer cannot be included in the pipeline; thus, the same transformation will be applied to both the modeled series and all exogenous variables. However, if the preprocessing transformations only apply to specific columns, then they need to be applied separately using <code>transformer_y</code> and <code>transformer_exog</code>.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: #000;\n",
       "  --sklearn-color-text-muted: #666;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: flex;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "  align-items: start;\n",
       "  justify-content: space-between;\n",
       "  gap: 0.5em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label .caption {\n",
       "  font-size: 0.6rem;\n",
       "  font-weight: lighter;\n",
       "  color: var(--sklearn-color-text-muted);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 0.5em;\n",
       "  text-align: center;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;scaler&#x27;, StandardScaler()), (&#x27;estimator&#x27;, Ridge())])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" ><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label  sk-toggleable__label-arrow\"><div><div>Pipeline</div></div><div><a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.6/modules/generated/sklearn.pipeline.Pipeline.html\">?<span>Documentation for Pipeline</span></a><span class=\"sk-estimator-doc-link \">i<span>Not fitted</span></span></div></label><div class=\"sk-toggleable__content \"><pre>Pipeline(steps=[(&#x27;scaler&#x27;, StandardScaler()), (&#x27;estimator&#x27;, Ridge())])</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-2\" type=\"checkbox\" ><label for=\"sk-estimator-id-2\" class=\"sk-toggleable__label  sk-toggleable__label-arrow\"><div><div>StandardScaler</div></div><div><a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.6/modules/generated/sklearn.preprocessing.StandardScaler.html\">?<span>Documentation for StandardScaler</span></a></div></label><div class=\"sk-toggleable__content \"><pre>StandardScaler()</pre></div> </div></div><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-3\" type=\"checkbox\" ><label for=\"sk-estimator-id-3\" class=\"sk-toggleable__label  sk-toggleable__label-arrow\"><div><div>Ridge</div></div><div><a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.6/modules/generated/sklearn.linear_model.Ridge.html\">?<span>Documentation for Ridge</span></a></div></label><div class=\"sk-toggleable__content \"><pre>Ridge()</pre></div> </div></div></div></div></div></div>"
      ],
      "text/plain": [
       "Pipeline(steps=[('scaler', StandardScaler()), ('estimator', Ridge())])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pipe = Pipeline(steps=[('scaler', StandardScaler()), ('estimator', Ridge())])\n",
    "pipe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "    <style>\n",
       "        .container-f4fbc866c1324f3c9ca263fccffc8a46 {\n",
       "            font-family: 'Arial', sans-serif;\n",
       "            font-size: 0.9em;\n",
       "            color: #333333;\n",
       "            border: 1px solid #ddd;\n",
       "            background-color: #f0f8ff;\n",
       "            padding: 5px 15px;\n",
       "            border-radius: 8px;\n",
       "            max-width: 600px;\n",
       "            #margin: auto;\n",
       "        }\n",
       "        .container-f4fbc866c1324f3c9ca263fccffc8a46 h2 {\n",
       "            font-size: 1.5em;\n",
       "            color: #222222;\n",
       "            border-bottom: 2px solid #ddd;\n",
       "            padding-bottom: 5px;\n",
       "            margin-bottom: 15px;\n",
       "            margin-top: 5px;\n",
       "        }\n",
       "        .container-f4fbc866c1324f3c9ca263fccffc8a46 details {\n",
       "            margin: 10px 0;\n",
       "        }\n",
       "        .container-f4fbc866c1324f3c9ca263fccffc8a46 summary {\n",
       "            font-weight: bold;\n",
       "            font-size: 1.1em;\n",
       "            color: #000000;\n",
       "            cursor: pointer;\n",
       "            margin-bottom: 5px;\n",
       "            background-color: #b3dbfd;\n",
       "            padding: 5px;\n",
       "            border-radius: 5px;\n",
       "        }\n",
       "        .container-f4fbc866c1324f3c9ca263fccffc8a46 summary:hover {\n",
       "            color: #000000;\n",
       "            background-color: #e0e0e0;\n",
       "        }\n",
       "        .container-f4fbc866c1324f3c9ca263fccffc8a46 ul {\n",
       "            font-family: 'Courier New', monospace;\n",
       "            list-style-type: none;\n",
       "            padding-left: 20px;\n",
       "            margin: 10px 0;\n",
       "            line-height: normal;\n",
       "        }\n",
       "        .container-f4fbc866c1324f3c9ca263fccffc8a46 li {\n",
       "            margin: 5px 0;\n",
       "            font-family: 'Courier New', monospace;\n",
       "        }\n",
       "        .container-f4fbc866c1324f3c9ca263fccffc8a46 li strong {\n",
       "            font-weight: bold;\n",
       "            color: #444444;\n",
       "        }\n",
       "        .container-f4fbc866c1324f3c9ca263fccffc8a46 li::before {\n",
       "            content: \"- \";\n",
       "            color: #666666;\n",
       "        }\n",
       "        .container-f4fbc866c1324f3c9ca263fccffc8a46 a {\n",
       "            color: #001633;\n",
       "            text-decoration: none;\n",
       "        }\n",
       "        .container-f4fbc866c1324f3c9ca263fccffc8a46 a:hover {\n",
       "            color: #359ccb; \n",
       "        }\n",
       "    </style>\n",
       "    \n",
       "        <div class=\"container-f4fbc866c1324f3c9ca263fccffc8a46\">\n",
       "            <p style=\"font-size: 1.5em; font-weight: bold; margin-block-start: 0.83em; margin-block-end: 0.83em;\">ForecasterRecursive</p>\n",
       "            <details open>\n",
       "                <summary>General Information</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Estimator:</strong> Pipeline</li>\n",
       "                    <li><strong>Lags:</strong> [ 1  2  3  4  5  6  7  8  9 10]</li>\n",
       "                    <li><strong>Window features:</strong> None</li>\n",
       "                    <li><strong>Window size:</strong> 10</li>\n",
       "                    <li><strong>Series name:</strong> y</li>\n",
       "                    <li><strong>Exogenous included:</strong> True</li>\n",
       "                    <li><strong>Weight function included:</strong> False</li>\n",
       "                    <li><strong>Differentiation order:</strong> None</li>\n",
       "                    <li><strong>Creation date:</strong> 2025-11-27 12:01:50</li>\n",
       "                    <li><strong>Last fit date:</strong> 2025-11-27 12:01:50</li>\n",
       "                    <li><strong>Skforecast version:</strong> 0.19.0</li>\n",
       "                    <li><strong>Python version:</strong> 3.12.11</li>\n",
       "                    <li><strong>Forecaster id:</strong> None</li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Exogenous Variables</summary>\n",
       "                <ul>\n",
       "                    exog_1, exog_2\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Data Transformations</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Transformer for y:</strong> None</li>\n",
       "                    <li><strong>Transformer for exog:</strong> None</li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Training Information</summary>\n",
       "                <ul>\n",
       "                    <li><strong>Training range:</strong> [Timestamp('1992-04-01 00:00:00'), Timestamp('2008-06-01 00:00:00')]</li>\n",
       "                    <li><strong>Training index type:</strong> DatetimeIndex</li>\n",
       "                    <li><strong>Training index frequency:</strong> <MonthBegin></li>\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Estimator Parameters</summary>\n",
       "                <ul>\n",
       "                    {'scaler__copy': True, 'scaler__with_mean': True, 'scaler__with_std': True, 'estimator__alpha': 1.0, 'estimator__copy_X': True, 'estimator__fit_intercept': True, 'estimator__max_iter': None, 'estimator__positive': False, 'estimator__random_state': None, 'estimator__solver': 'auto', 'estimator__tol': 0.0001}\n",
       "                </ul>\n",
       "            </details>\n",
       "            <details>\n",
       "                <summary>Fit Kwargs</summary>\n",
       "                <ul>\n",
       "                    {}\n",
       "                </ul>\n",
       "            </details>\n",
       "            <p>\n",
       "                <a href=\"https://skforecast.org/0.19.0/api/forecasterrecursive.html\">&#128712 <strong>API Reference</strong></a>\n",
       "                &nbsp;&nbsp;\n",
       "                <a href=\"https://skforecast.org/0.19.0/user_guides/autoregressive-forecaster.html\">&#128462 <strong>User Guide</strong></a>\n",
       "            </p>\n",
       "        </div>\n",
       "        "
      ],
      "text/plain": [
       "=================== \n",
       "ForecasterRecursive \n",
       "=================== \n",
       "Estimator: Pipeline \n",
       "Lags: [ 1  2  3  4  5  6  7  8  9 10] \n",
       "Window features: None \n",
       "Window size: 10 \n",
       "Series name: y \n",
       "Exogenous included: True \n",
       "Exogenous names: exog_1, exog_2 \n",
       "Transformer for y: None \n",
       "Transformer for exog: None \n",
       "Weight function included: False \n",
       "Differentiation order: None \n",
       "Training range: [Timestamp('1992-04-01 00:00:00'), Timestamp('2008-06-01 00:00:00')] \n",
       "Training index type: DatetimeIndex \n",
       "Training index frequency: <MonthBegin> \n",
       "Estimator parameters: \n",
       "    {'scaler__copy': True, 'scaler__with_mean': True, 'scaler__with_std': True,\n",
       "    'estimator__alpha': 1.0, 'estimator__copy_X': True,\n",
       "    'estimator__fit_intercept': True, 'estimator__max_iter': None,\n",
       "    'estimator__positive': False, 'estimator__random_state': None,\n",
       "    'estimator__solver': 'auto', 'estimator__tol': 0.0001} \n",
       "fit_kwargs: {} \n",
       "Creation date: 2025-11-27 12:01:50 \n",
       "Last fit date: 2025-11-27 12:01:50 \n",
       "Skforecast version: 0.19.0 \n",
       "Python version: 3.12.11 \n",
       "Forecaster id: None "
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create and fit forecaster\n",
    "# ==============================================================================\n",
    "forecaster = ForecasterRecursive(\n",
    "                 estimator = pipe,\n",
    "                 lags      = 10\n",
    "             )\n",
    "\n",
    "forecaster.fit(y=data['y'], exog=data[['exog_1', 'exog_2']])\n",
    "forecaster"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When performing a grid search over a scikit-learn pipeline, the model's name precedes the parameters' name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "aae45ba4b57a4f2f9dd835c53dcc1895",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "lags grid:   0%|          | 0/3 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "e37bc80f2178438d99cdcce35d20c5c9",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "params grid:   0%|          | 0/10 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "`Forecaster` refitted using the best-found lags and parameters, and the whole data set: \n",
      "  Lags: [1 2 3 4 5] \n",
      "  Parameters: {'estimator__alpha': np.float64(0.001)}\n",
      "  Backtesting metric: 6.845311709556121e-05\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>lags</th>\n",
       "      <th>lags_label</th>\n",
       "      <th>params</th>\n",
       "      <th>mean_absolute_error</th>\n",
       "      <th>estimator__alpha</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>[1, 2, 3, 4, 5]</td>\n",
       "      <td>[1, 2, 3, 4, 5]</td>\n",
       "      <td>{'estimator__alpha': 0.001}</td>\n",
       "      <td>0.000068</td>\n",
       "      <td>0.001000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14...</td>\n",
       "      <td>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14...</td>\n",
       "      <td>{'estimator__alpha': 0.001}</td>\n",
       "      <td>0.000188</td>\n",
       "      <td>0.001000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>[1, 2, 3, 4, 5]</td>\n",
       "      <td>[1, 2, 3, 4, 5]</td>\n",
       "      <td>{'estimator__alpha': 0.007742636826811269}</td>\n",
       "      <td>0.000526</td>\n",
       "      <td>0.007743</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14...</td>\n",
       "      <td>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14...</td>\n",
       "      <td>{'estimator__alpha': 0.007742636826811269}</td>\n",
       "      <td>0.001413</td>\n",
       "      <td>0.007743</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                                lags  \\\n",
       "0                                    [1, 2, 3, 4, 5]   \n",
       "1  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14...   \n",
       "2                                    [1, 2, 3, 4, 5]   \n",
       "3  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14...   \n",
       "\n",
       "                                          lags_label  \\\n",
       "0                                    [1, 2, 3, 4, 5]   \n",
       "1  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14...   \n",
       "2                                    [1, 2, 3, 4, 5]   \n",
       "3  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14...   \n",
       "\n",
       "                                       params  mean_absolute_error  \\\n",
       "0                 {'estimator__alpha': 0.001}             0.000068   \n",
       "1                 {'estimator__alpha': 0.001}             0.000188   \n",
       "2  {'estimator__alpha': 0.007742636826811269}             0.000526   \n",
       "3  {'estimator__alpha': 0.007742636826811269}             0.001413   \n",
       "\n",
       "   estimator__alpha  \n",
       "0          0.001000  \n",
       "1          0.001000  \n",
       "2          0.007743  \n",
       "3          0.007743  "
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Hyperparameter grid search using a scikit-learn pipeline\n",
    "# ==============================================================================\n",
    "pipe = Pipeline(steps=[('scaler', StandardScaler()), ('estimator', Ridge())])\n",
    "forecaster = ForecasterRecursive(\n",
    "                 estimator = pipe,\n",
    "                 lags = 10  # This value will be replaced in the grid search\n",
    "             )\n",
    "\n",
    "# Estimator's hyperparameters\n",
    "param_grid = {'estimator__alpha': np.logspace(-3, 5, 10)}\n",
    "\n",
    "# Lags used as predictors\n",
    "lags_grid = [5, 24, [1, 2, 3, 23, 24]]\n",
    "\n",
    "cv = TimeSeriesFold(\n",
    "    steps=5, initial_train_size=len(data.loc[:'2000-04-01']), refit=False\n",
    ")\n",
    "\n",
    "results_grid = grid_search_forecaster(\n",
    "                   forecaster         = forecaster,\n",
    "                   y                  = data['y'],\n",
    "                   exog               = data[['exog_1', 'exog_2']],\n",
    "                   param_grid         = param_grid,\n",
    "                   lags_grid          = lags_grid,\n",
    "                   cv                 = cv,\n",
    "                   metric             = 'mean_absolute_error'\n",
    "               )\n",
    "\n",
    "results_grid.head(4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Transforming multiple input series in global models\n",
    "\n",
    "When using global forecasting models (<a href=\"../user_guides/independent-multi-time-series-forecasting.html#scikit-learn-transformers-in-multi-series\">ForecasterRecursiveMultiSeries</a> or <a href=\"../user_guides/dependent-multi-series-multivariate-forecasting.html#scikit-learn-transformers-in-multivariate\">ForecasterDirectMultiVariate</a>) the <code>transformer_series</code> argument replaces <code>transformer_y</code>. Three different options are available:\n",
    "\n",
    "+ `transformer_series` is a single transformer: When a single transformer is provided, it is automatically cloned for each individual series. Each cloned transformer is then trained separately on one of the series.\n",
    "\n",
    "+ `transformer_series` is a dictionary: A different transformer can be specified for each series by passing a dictionary where the keys correspond to the series names and the values are the transformers. Each series is transformed according to its designated transformer. When this option is used, it is mandatory to include a transformer for unknown series, which is indicated by the key `'_unknown_level'`.\n",
    "\n",
    "+ `transformer_series` is None: no transformations are applied to any of the series.\n",
    "\n",
    "Regardless of the configuration, each series is transformed independently. Even when using a single transformer, it is cloned internally and applied separately to each series."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">╭─────────────────────── <span style=\"font-weight: bold\">items_sales</span> ───────────────────────╮\n",
       "│ <span style=\"font-weight: bold\">Description:</span>                                              │\n",
       "│ Simulated time series for the sales of 3 different items. │\n",
       "│                                                           │\n",
       "│ <span style=\"font-weight: bold\">Source:</span>                                                   │\n",
       "│ Simulated data.                                           │\n",
       "│                                                           │\n",
       "│ <span style=\"font-weight: bold\">URL:</span>                                                      │\n",
       "│ https://raw.githubusercontent.com/skforecast/skforecast-  │\n",
       "│ datasets/main/data/simulated_items_sales.csv              │\n",
       "│                                                           │\n",
       "│ <span style=\"font-weight: bold\">Shape:</span> 1097 rows x 3 columns                              │\n",
       "╰───────────────────────────────────────────────────────────╯\n",
       "</pre>\n"
      ],
      "text/plain": [
       "╭─────────────────────── \u001b[1mitems_sales\u001b[0m ───────────────────────╮\n",
       "│ \u001b[1mDescription:\u001b[0m                                              │\n",
       "│ Simulated time series for the sales of 3 different items. │\n",
       "│                                                           │\n",
       "│ \u001b[1mSource:\u001b[0m                                                   │\n",
       "│ Simulated data.                                           │\n",
       "│                                                           │\n",
       "│ \u001b[1mURL:\u001b[0m                                                      │\n",
       "│ https://raw.githubusercontent.com/skforecast/skforecast-  │\n",
       "│ datasets/main/data/simulated_items_sales.csv              │\n",
       "│                                                           │\n",
       "│ \u001b[1mShape:\u001b[0m 1097 rows x 3 columns                              │\n",
       "╰───────────────────────────────────────────────────────────╯\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>item_1</th>\n",
       "      <th>item_2</th>\n",
       "      <th>item_3</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>date</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>2012-01-01</th>\n",
       "      <td>8.253175</td>\n",
       "      <td>21.047727</td>\n",
       "      <td>19.429739</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2012-01-02</th>\n",
       "      <td>22.777826</td>\n",
       "      <td>26.578125</td>\n",
       "      <td>28.009863</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2012-01-03</th>\n",
       "      <td>27.549099</td>\n",
       "      <td>31.751042</td>\n",
       "      <td>32.078922</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2012-01-04</th>\n",
       "      <td>25.895533</td>\n",
       "      <td>24.567708</td>\n",
       "      <td>27.252276</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2012-01-05</th>\n",
       "      <td>21.379238</td>\n",
       "      <td>18.191667</td>\n",
       "      <td>20.357737</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "               item_1     item_2     item_3\n",
       "date                                       \n",
       "2012-01-01   8.253175  21.047727  19.429739\n",
       "2012-01-02  22.777826  26.578125  28.009863\n",
       "2012-01-03  27.549099  31.751042  32.078922\n",
       "2012-01-04  25.895533  24.567708  27.252276\n",
       "2012-01-05  21.379238  18.191667  20.357737"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Data download\n",
    "# ==============================================================================\n",
    "data = fetch_dataset(name=\"items_sales\")\n",
    "data.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">╭────────────────────────────────── InputTypeWarning ──────────────────────────────────╮</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> Passing a DataFrame (either wide or long format) as `series` requires additional     <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> internal transformations, which can increase computational time. It is recommended   <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> to use a dictionary of pandas Series instead. For more details, see:                 <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> https://skforecast.org/latest/user_guides/independent-multi-time-series-forecasting. <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> html#input-data                                                                      <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>                                                                                      <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> Category : skforecast.exceptions.InputTypeWarning                                    <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> Location :                                                                           <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> c:\\Users\\jaesc2\\Miniconda3\\envs\\skforecast_py12\\Lib\\site-packages\\skforecast\\utils\\u <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> tils.py:2349                                                                         <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> Suppress : warnings.simplefilter('ignore', category=InputTypeWarning)                <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">╰──────────────────────────────────────────────────────────────────────────────────────╯</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[38;5;214m╭─\u001b[0m\u001b[38;5;214m─────────────────────────────────\u001b[0m\u001b[38;5;214m InputTypeWarning \u001b[0m\u001b[38;5;214m─────────────────────────────────\u001b[0m\u001b[38;5;214m─╮\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m Passing a DataFrame (either wide or long format) as `series` requires additional     \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m internal transformations, which can increase computational time. It is recommended   \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m to use a dictionary of pandas Series instead. For more details, see:                 \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m https://skforecast.org/latest/user_guides/independent-multi-time-series-forecasting. \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m html#input-data                                                                      \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m                                                                                      \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m Category : skforecast.exceptions.InputTypeWarning                                    \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m Location :                                                                           \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m c:\\Users\\jaesc2\\Miniconda3\\envs\\skforecast_py12\\Lib\\site-packages\\skforecast\\utils\\u \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m tils.py:2349                                                                         \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m Suppress : warnings.simplefilter('ignore', category=InputTypeWarning)                \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m╰──────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Series transformation: same transformation for all series\n",
    "# ==============================================================================\n",
    "forecaster = ForecasterRecursiveMultiSeries(\n",
    "                 estimator          = LGBMRegressor(random_state=123, verbose=-1),\n",
    "                 lags               = 24,\n",
    "                 encoding           = 'ordinal',\n",
    "                 transformer_series = StandardScaler(),\n",
    "                 transformer_exog   = None\n",
    "             )\n",
    "forecaster.fit(series=data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is possible to access the fitted transformers for each series through the `transformers_series_` attribute. This allows verification that each transformer has been trained independently.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Series item_1: StandardScaler() mean=[22.37366364], scale=[2.54258317]\n",
      "Series item_2: StandardScaler() mean=[16.26942518], scale=[4.89965692]\n",
      "Series item_3: StandardScaler() mean=[17.19276546], scale=[5.43694388]\n",
      "Series _unknown_level: StandardScaler() mean=[18.61195143], scale=[5.21803675]\n"
     ]
    }
   ],
   "source": [
    "# Mean and scale of the transformer for each series\n",
    "# ==============================================================================\n",
    "for k, v in forecaster.transformer_series_.items():\n",
    "    print(f\"Series {k}: {v} mean={v.mean_}, scale={v.scale_}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">╭────────────────────────────────── InputTypeWarning ──────────────────────────────────╮</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> Passing a DataFrame (either wide or long format) as `series` requires additional     <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> internal transformations, which can increase computational time. It is recommended   <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> to use a dictionary of pandas Series instead. For more details, see:                 <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> https://skforecast.org/latest/user_guides/independent-multi-time-series-forecasting. <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> html#input-data                                                                      <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>                                                                                      <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> Category : skforecast.exceptions.InputTypeWarning                                    <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> Location :                                                                           <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> c:\\Users\\jaesc2\\Miniconda3\\envs\\skforecast_py12\\Lib\\site-packages\\skforecast\\utils\\u <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> tils.py:2349                                                                         <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> Suppress : warnings.simplefilter('ignore', category=InputTypeWarning)                <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">╰──────────────────────────────────────────────────────────────────────────────────────╯</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[38;5;214m╭─\u001b[0m\u001b[38;5;214m─────────────────────────────────\u001b[0m\u001b[38;5;214m InputTypeWarning \u001b[0m\u001b[38;5;214m─────────────────────────────────\u001b[0m\u001b[38;5;214m─╮\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m Passing a DataFrame (either wide or long format) as `series` requires additional     \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m internal transformations, which can increase computational time. It is recommended   \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m to use a dictionary of pandas Series instead. For more details, see:                 \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m https://skforecast.org/latest/user_guides/independent-multi-time-series-forecasting. \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m html#input-data                                                                      \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m                                                                                      \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m Category : skforecast.exceptions.InputTypeWarning                                    \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m Location :                                                                           \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m c:\\Users\\jaesc2\\Miniconda3\\envs\\skforecast_py12\\Lib\\site-packages\\skforecast\\utils\\u \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m tils.py:2349                                                                         \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m Suppress : warnings.simplefilter('ignore', category=InputTypeWarning)                \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m╰──────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">╭─────────────────────────────── IgnoredArgumentWarning ───────────────────────────────╮</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> {'item_3'} not present in `transformer_series`. No transformation is applied to      <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> these series.                                                                        <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>                                                                                      <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> Category : skforecast.exceptions.IgnoredArgumentWarning                              <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> Location :                                                                           <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> c:\\Users\\jaesc2\\Miniconda3\\envs\\skforecast_py12\\Lib\\site-packages\\skforecast\\utils\\u <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> tils.py:371                                                                          <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span> Suppress : warnings.simplefilter('ignore', category=IgnoredArgumentWarning)          <span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">│</span>\n",
       "<span style=\"color: #ffaf00; text-decoration-color: #ffaf00\">╰──────────────────────────────────────────────────────────────────────────────────────╯</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[38;5;214m╭─\u001b[0m\u001b[38;5;214m──────────────────────────────\u001b[0m\u001b[38;5;214m IgnoredArgumentWarning \u001b[0m\u001b[38;5;214m──────────────────────────────\u001b[0m\u001b[38;5;214m─╮\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m {'item_3'} not present in `transformer_series`. No transformation is applied to      \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m these series.                                                                        \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m                                                                                      \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m Category : skforecast.exceptions.IgnoredArgumentWarning                              \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m Location :                                                                           \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m c:\\Users\\jaesc2\\Miniconda3\\envs\\skforecast_py12\\Lib\\site-packages\\skforecast\\utils\\u \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m tils.py:371                                                                          \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m│\u001b[0m Suppress : warnings.simplefilter('ignore', category=IgnoredArgumentWarning)          \u001b[38;5;214m│\u001b[0m\n",
       "\u001b[38;5;214m╰──────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Series transformation: different transformation for each series\n",
    "# ==============================================================================\n",
    "forecaster = ForecasterRecursiveMultiSeries(\n",
    "                 estimator          = LGBMRegressor(random_state=123, verbose=-1),\n",
    "                 lags               = 24,\n",
    "                 encoding           = 'ordinal',\n",
    "                 transformer_series = {'item_1': StandardScaler(), 'item_2': MinMaxScaler(), '_unknown_level': StandardScaler()},\n",
    "                 transformer_exog   = None\n",
    "             )\n",
    "\n",
    "forecaster.fit(series=data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Series item_1: {'copy': True, 'with_mean': True, 'with_std': True}\n",
      "Series item_2: {'clip': False, 'copy': True, 'feature_range': (0, 1)}\n",
      "Series item_3: None\n",
      "Series _unknown_level: {'copy': True, 'with_mean': True, 'with_std': True}\n"
     ]
    }
   ],
   "source": [
    "# Transformer trained for each series\n",
    "# ==============================================================================\n",
    "for k, v in forecaster.transformer_series_.items():\n",
    "    if v is not None:\n",
    "        print(f\"Series {k}: {v.get_params()}\")\n",
    "    else:\n",
    "        print(f\"Series {k}: {v}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "skforecast_py12",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": true,
   "title_cell": "Tabla de contenidos",
   "title_sidebar": "Tabla de contenidos",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "165px"
   },
   "toc_section_display": true,
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "position": {
    "height": "144.391px",
    "left": "1478px",
    "right": "20px",
    "top": "126px",
    "width": "350px"
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
